import pytest
from unittest.mock import Mock, patch

from motor.controller.ft.strategy.strategy import (
    StrategyBase,
    general_L0_strategy,
    specific_L2_strategy,
    general_L3_to_L6_strategy,
    generate_strategy_map,
)
from motor.controller.ft.strategy.lingqu_network_recover import (
    LingquNetworkRecoverStrategy,
)
from motor.controller.ft.strategy.scale_p2d import ScaleP2DStrategy


class TestStrategyBase:
    """Test StrategyBase class"""
    
    def test_strategy_base_execute_raises_not_implemented_error(self):
        """StrategyBase.execute should raise NotImplementedError"""
        # Create a concrete implementation class to test the abstract method
        class ConcreteStrategy(StrategyBase):
            def execute(self, id):
                raise NotImplementedError("execute method not implemented")
            
            def stop(self):
                raise NotImplementedError("stop method not implemented")
        
        base = ConcreteStrategy()
        with pytest.raises(NotImplementedError):
            base.execute(id=None)  # type: ignore[arg-type]


class TestGeneralL0Strategy:
    """Test L0 general strategy functionality"""

    def test_returns_none_for_any_fault_code(self):
        """L0 strategy should always return None since it represents healthy state"""
        assert general_L0_strategy(0x0000, 1) is None
        assert general_L0_strategy(0x00f1fef5, 1) is None
        assert general_L0_strategy(0x08520003, 1) is None
        assert general_L0_strategy(0x12345678, 1) is None


class TestSpecificL2Strategy:
    """Test L2 specific strategy functionality"""
    
    def test_returns_expected_class_for_known_error_codes(self):
        """For known L2 error codes, the strategy function should return LingquNetworkRecoverStrategy class"""
        assert specific_L2_strategy(0x00f1fef5, 1) is LingquNetworkRecoverStrategy
        assert specific_L2_strategy(0x08520003, 1) is LingquNetworkRecoverStrategy

    def test_returns_none_for_unknown_error_codes(self):
        """For unknown L2 error codes, the strategy function should return None"""
        assert specific_L2_strategy(0, 1) is None
        assert specific_L2_strategy(0x0000, 1) is None


class TestGeneralL3ToL6Strategy:
    """Test L3-L6 general strategy functionality"""
    
    @patch('motor.controller.core.instance_manager.InstanceManager')
    def test_returns_scale_p2d_for_decode_role(self, mock_instance_manager):
        """When the instance role is decode, the strategy function should return ScaleP2DStrategy"""
        # Mock instance manager to return decode role instance
        mock_instance = Mock()
        mock_instance.role = "decode"
        mock_instance_manager.return_value.get_instance.return_value = mock_instance
        
        result = general_L3_to_L6_strategy(0x0000, 1)
        assert result is ScaleP2DStrategy

    @patch('motor.controller.core.instance_manager.InstanceManager')
    def test_returns_none_for_non_decode_role(self, mock_instance_manager):
        """When the instance role is not decode, the strategy function should return None"""
        # Mock instance manager to return non-decode role instance
        mock_instance = Mock()
        mock_instance.role = "encode"
        mock_instance_manager.return_value.get_instance.return_value = mock_instance
        
        result = general_L3_to_L6_strategy(0x0000, 1)
        assert result is None

    @patch('motor.controller.core.instance_manager.InstanceManager')
    def test_returns_none_when_instance_is_none(self, mock_instance_manager):
        """When the instance is None, the strategy function should return None"""
        # Mock instance manager to return None
        mock_instance_manager.return_value.get_instance.return_value = None
        
        result = general_L3_to_L6_strategy(0x0000, 1)
        assert result is None


class TestGenerateStrategyMap:
    """Test strategy mapping generation functionality"""
    
    def test_mapping_contains_expected_levels(self):
        """Verify that the mapping contains the expected levels"""
        strategies = generate_strategy_map()
        assert set(strategies.keys()) == {"L0", "L1", "L2", "L3", "L4", "L5", "L6"}

    def test_l0_strategy_returns_none(self):
        """L0 strategy should return None"""
        strategies = generate_strategy_map()
        l0_func = strategies["L0"]
        assert callable(l0_func)
        assert l0_func(0x0000, 1) is None

    def test_l1_strategy_returns_none(self):
        """L1 strategy should return None"""
        strategies = generate_strategy_map()
        l1_func = strategies["L1"]
        assert callable(l1_func)
        assert l1_func(0x0000, 1) is None

    def test_l2_strategy_maps_to_specific_resolver(self):
        """L2 mapping to specific L2 resolver function"""
        strategies = generate_strategy_map()
        l2_func = strategies["L2"]
        assert callable(l2_func)
        assert l2_func(0x00f1fef5, 1) is LingquNetworkRecoverStrategy
        assert l2_func(0x08520003, 1) is LingquNetworkRecoverStrategy
        assert l2_func(0, 1) is None

    @patch('motor.controller.core.instance_manager.InstanceManager')
    def test_l3_to_l6_strategies_map_to_general_resolver(self, mock_instance_manager):
        """L3-L6 mapping to general resolver function"""
        # Mock instance manager to return decode role instance
        mock_instance = Mock()
        mock_instance.role = "decode"
        mock_instance_manager.return_value.get_instance.return_value = mock_instance
        
        strategies = generate_strategy_map()
        
        for level in ("L3", "L4", "L5", "L6"):
            resolver = strategies[level]
            assert callable(resolver)
            assert resolver(0x0000, 1) is ScaleP2DStrategy